package com.swfever.fs.commons.core.seed
{
	import cmodule.as3_crypto_wrapper.CLibInit;
	
	import flash.events.EventDispatcher;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	[Event(name="complete",type="com.swfever.fs.commons.core.seed.SeedFileCreationEvent")]
	[Event(name="cancelled",type="com.swfever.fs.commons.core.seed.SeedFileCreationEvent")]
	[Event(name="failed",type="com.swfever.fs.commons.core.seed.SeedFileCreationEvent")]
	public class SeedFileCreator extends EventDispatcher
	{
		
		private var CLib:*;
		
		private static const DEFAULT_BLOCK_SIZE:uint = 32 * 1024;
		private static const BUFFER_SIZE:uint = 16;
			
		private var _isBusy:Boolean;
		private var _proceededBytes:uint;
		
		private var file:File;
		private var fs:FileStream;
		private var fileName:String;
		private var fileSize:uint;
		private var blockSize:uint;
		private var digestTable:Array;
		
		private var readBuffer:ByteArray;
		private var readTimer:Timer;
		

		public function SeedFileCreator()
		{
			CLib = new CLibInit().init();
			
			readBuffer = new ByteArray();
			readTimer = new Timer(40);
			readTimer.addEventListener(TimerEvent.TIMER, readTimer_onTimer);
			
			fs = new FileStream();
		}
		
		public function get isBusy():Boolean {
			return _isBusy;
		}
		
		public function get proceededBytes():uint {
			return _proceededBytes;
		}
		
		public function get totalBytes():uint {
			return fileSize;
		}
		
		public function create(fileURL:String):void {
			if(isBusy) {
				return;
			}
			
			file = new File(fileURL);
			fileSize = file.size;
			blockSize = DEFAULT_BLOCK_SIZE;
			fileName = file.name;
			readBuffer.clear();
			digestTable = new Array();
			try {
				fs.readAhead = BUFFER_SIZE * blockSize;
				fs.openAsync(file,FileMode.READ);
			} catch(e:Error) {
				return;
			}

			_isBusy = true;
			_proceededBytes = 0;
			
			if(!readTimer.running) {
				readTimer.start();
			}

		}
		
		public function cancel():void {
			if(isBusy) {
				cleanUp();
				dispatchEvent(new SeedFileCreationEvent(SeedFileCreationEvent.CANCELLED));
			}
		}
		
		private function readTimer_onTimer(event:TimerEvent):void {
			var restBytes:uint = fileSize - fs.position;
			if(fs.bytesAvailable >= fs.readAhead || (restBytes < fs.readAhead && fs.bytesAvailable == restBytes)) {
				fs.readBytes(readBuffer,0, Math.min(fs.readAhead, restBytes));
				var block:ByteArray = new ByteArray();
				var bytesToProceed:uint;
				while(readBuffer.bytesAvailable) {
					bytesToProceed = Math.min(blockSize,readBuffer.bytesAvailable);
					readBuffer.readBytes(block,0,bytesToProceed); 
					// md5 hash
					var handle:uint = CLib.md5_begin();
					CLib.md5_update(handle, block);
					digestTable.push(CLib.md5_finish(handle));
					_proceededBytes += bytesToProceed;
				}
				readBuffer.clear();
				if(fs.position == fileSize) {
					var data:ByteArray = generateSeedFileData();
					cleanUp();
					dispatchEvent(new SeedFileCreationEvent(SeedFileCreationEvent.COMPLETE,data));
				}
			}
		}
		
		private function generateSeedFileData():ByteArray {
			var data:ByteArray = new ByteArray();
			data.writeObject(digestTable);
			data.compress();
			return data;
		}
		
		private function cleanUp():void {
			fs.close()
			readTimer.stop();
			_isBusy = false;		
			digestTable.splice(0, digestTable.length);
		}
	}
}